home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1996 #15 / Monster Media Number 15 (Monster Media)(July 1996).ISO / win_utl2 / wasted16.zip / WASTED.PAS < prev    next >
Pascal/Delphi Source File  |  1996-05-17  |  19KB  |  438 lines

  1. { WASTED.EXE - Version 1.6    }
  2. { Created: 05/17/1996         }
  3. { Writen by Tim Jones         }
  4. { tjones@wpogate.ssc.nasa.gov }
  5.  
  6. {$M 32767,0,655360}          {set up a large stack for recursion}
  7.  
  8. Program wasted;
  9. uses
  10.   dos;                       {for things like findfirst/findnext, intr() etc.}
  11. const
  12.   version='1.6';             {current version of the program}
  13.   fspec='*.*';               {could be changed to be a user input ex. *.exe}
  14.   lastrptm=2;                {number of report methods in the program}
  15. var
  16.   param:string;              {holding variable for each cmd-line parameter}
  17.   startDir,origDir:string;   {dir to start checking at and the dir we are in}
  18.   totclust:longint;          {total number of clusters on the disk}
  19.   clust,usrclust:longint;    {cluster size, user cluster size}
  20.   totalbs:longint;           {total bytesize for all directories}
  21.   totalrs:longint;           {total realsize for all directories}
  22.   regs:registers;            {variable for holding DOS Interrupt registers}
  23.   i:byte;                    {loop variable}
  24.   fcount:byte;               {# of files specified on the cmd-line}
  25.   sdrive:byte;               {source drive in numerical format A=1, B=2 etc.}
  26.   s1name,source:dirstr;      {user specified dir and actual dir to be used}
  27.   sdir:dirstr;               {pieces parts for the split source dir}
  28.   sname:namestr;             {pieces parts for the split source dir(not used)}
  29.   sext:extstr;               {pieces parts for the split source dir(not used)}
  30.   error:integer;             {string to numeric conversion error flag}
  31.   showtotals:boolean;        {holds user choice for showing totals or not}
  32.   pause:boolean;             {holds user choice for screen pausing}
  33.   linecount:byte;            {holds the number of screen lines displayed}
  34.   quitit:boolean;            {if quitit is true then the program will end}
  35.   usrrptm:integer;           {user specified report method}
  36.   tmp:string;                {used to hold numbers for conversion to strings}
  37.  
  38. {This function will convert a given string into an all upper case string}
  39. function upper(s:string):string;
  40. var
  41.   i:Integer;
  42. begin
  43.   for i := 1 to Length(s) do
  44.     s[i] := UpCase(s[i]);
  45.   upper:=s;
  46. end;
  47.  
  48. {This function will "pad right" a string to a given length with a given char.}
  49. function padr(s:string; l:integer; c:char):string;
  50. begin
  51.   while length(s) < l do
  52.   begin
  53.     s:=s+c;
  54.   end;
  55.   padr:=s;
  56. end;
  57.  
  58.  
  59. {This function will return the left portion of a string (chars 1 to p)}
  60. function left(s:string; p:integer):string;
  61. begin
  62.   if p > 0 then
  63.     delete(s, p+1, length(s)-p)
  64.   else
  65.     s:='';
  66.   left:=s;
  67. end;
  68.  
  69.  
  70. {This function will return the right portion of a string (chars strlen to p)}
  71. function right(s:string; p:integer):string;
  72. begin
  73.   if p > 0 then
  74.     delete(s, 1, length(s)-p)
  75.   else
  76.     s:='';
  77.   right:=s;
  78. end;
  79.  
  80.  
  81. {This function invokes DOS interrupt 21h function 1Ch to return the cluster}
  82. {size for the given drive}
  83. {bytedrv = numerical equivalent of the drive letter A=0, B=1, C=2 etc.}
  84. function getClustSize(bytedrv:byte):word;
  85. var
  86.   regs:registers;
  87. begin
  88.   regs.AH:=$1C;                     {Call to Get FAT info}
  89.   regs.DL:=bytedrv;                 {function 1C expects A=1, B=2, C=3 etc.}
  90.   intr($21,regs);                   {perform actual interrupt call}
  91.   getClustSize:=regs.al*regs.cx;    {calculate/return cluster size}
  92. end;
  93.  
  94.  
  95. {This function invokes DOS interrupt 21h function 36h to return the number}
  96. {of used clusters on the given drive}
  97. {bytedrv = numerical equivalent of the drive letter A=0, B=1, C=2 etc.}
  98. function getTotalClust(bytedrv:byte):word;
  99. var
  100.   regs:registers;
  101. begin
  102.   regs.AH:=$36;                     {Call to Get Free Disk Space}
  103.   regs.DL:=bytedrv;                 {function 36 expects A=1, B=2, C=3 etc.}
  104.   intr($21,regs);                   {perform actual interrupt call}
  105.   getTotalClust:=regs.dx;           {calculate/return no. of used clusters}
  106. end;
  107.  
  108.  
  109. {This function will make sure that the given directory has at least one}
  110. {directory specified and will chop off the trailing backslash if needed}
  111. {This function was necessary because the pascal CHDIR() function does not}
  112. {appear to work properly.  ex. chdir('C:\') works but chdir('C:\BP\BIN\')}
  113. {does not work.  (go figure)}
  114. function dir(sdir:string):string;
  115. begin
  116.   if right(sdir,1)='\' then        {check for an ending backslash}
  117.   begin
  118.     sdir:=left(sdir, length(sdir)-1);  {remove the ending backslash}
  119.     if right(sdir,1)=':' then      {check for the case of "C:\"}
  120.       sdir:=sdir+'\';              {put the ending backslash back on}
  121.   end;
  122.   dir:=sdir;  {return the new directory}
  123. end;
  124.  
  125. {This procedure will display the syntax for the program and also display an}
  126. {error message if one was supplied.  This procedure always halts the program}
  127. {with an error level 0 (no error) or 1 (error)}
  128. procedure syntax(errmsg:string);
  129. begin
  130.   writeln(' Purpose:');
  131.   writeln('   WASTED was written to quickly traverse a harddrive and report');
  132.   writeln('   how much diskspace each directory really uses based upon the');
  133.   writeln('   cluster size.');
  134.   writeln(' Output:');
  135.   writeln('   dirbytes  usedbytes  usedbytes-dirbytes  percentage');
  136.   writeln('   where percentage is based upon the report method used. (see below)');
  137.   writeln(' Syntax:');
  138.   writeln('   WASTED [directory] [parameters]');
  139.   writeln('   where directory is the directory to start at (default=current directory)');
  140.   writeln(' Parameters: (prefix / and - are valid)');
  141.   writeln('   /? = This help text');
  142.   writeln('   /C:n = Sets the cluster size to n (where n > 0)');
  143.   writeln('   /NT  = Turns off the displaying of the Total line (No Totals)');
  144.   writeln('   /P   = Pause between screens');
  145.   writeln('   /R:n = Report using method n');
  146.   writeln('          n=1 = Wasted percentage based upon disk size (default)');
  147.   writeln('          n=2 = Wasted percentage based upon used disk space');
  148.   writeln(' optional parameters may be specified in any order');
  149.   writeln;
  150.   if errmsg <> '' then
  151.   begin
  152.     writeln;
  153.     writeln('Error: '+errmsg);
  154.     halt(1);
  155.   end;
  156.   halt(0);
  157. end;
  158.  
  159.  
  160. {This function takes a given drive/directory and figures out the cluster info}
  161. function init(startdir:string):string;
  162. var
  163.   source:string[80];
  164. begin
  165.   {generate the source drive (default = current drive\dir)}
  166.   if right(startdir,1)<>'\' then startdir:=startdir+'\'; {append an ending \}
  167.   source:=fexpand(startdir);       {expands the dir to a fully qualified dir}
  168.   fsplit(source,sdir,sname,sext);  {splits the dir into pieces parts}
  169.   sdrive:=ord(upcase(sdir[1]))-64; {store the source drive as a number A=1}
  170.   clust:=getClustSize(sdrive);     {store the cluster size}
  171.   totclust:=getTotalClust(sdrive); {store the total number of clusters}
  172.   if usrclust > 0 then             {if the user specified a cluster size...}
  173.   begin
  174.     {calc how many user clusters will fit on the drive}
  175.     totclust:=(totclust*clust) div usrclust;
  176.     clust:=usrclust;               {use the users cluster size}
  177.   end;
  178.   totalbs:=0;                      {initialize the total bytesize}
  179.   totalrs:=0;                      {initialize the total realsize}
  180.   linecount:=0;                    {initialize the number of lines displayed}
  181.   quitit:=false;                   {allow recursion to start}
  182.   init:=sdir;                      {return the drive/dir we just got info on}
  183. end;
  184.  
  185.  
  186. {This function invokes DOS interrupt 21h function 07h to wait for user input}
  187. {from STDIN.  The reason I just didn't use Pascal's Readkey or Keypressed}
  188. {functions is because if you include the CRT unit, then you cannot perform}
  189. {redirection of output on the dos commandline ex. WASTED C:\ > FULLDISK.TXT}
  190. function dosPause:byte;
  191. var
  192.   regs:registers;
  193. begin
  194.   regs.AH:=$07;                     {Call to Direct STDIN Input function}
  195.   intr($21,regs);                   {perform actual interrupt call}
  196.   if regs.AL = $1B then             {check for ESC key}
  197.     quitit:=true;                   {set flag to break out of recursion loop}
  198. end;
  199.  
  200.  
  201. procedure checkpause(lines:byte);
  202. var
  203.   k:char;
  204. begin
  205.   if pause = true then
  206.   begin
  207.     if linecount+lines > 23 then
  208.     begin
  209.       dospause;
  210.       linecount:=0;
  211.     end;
  212.     linecount:=linecount+lines;
  213.   end;
  214. end;
  215.  
  216.  
  217. {This procedure will calculate the amount of wasted space taken up by the}
  218. {files in the current directory}
  219. procedure calcWaste(fspec:string);
  220. var
  221.   dirinfo:searchrec; {search record for findfirst/findnext functions}
  222.   bytesize:longint;  {combined byte size of the files as reported by DOS}
  223.   realsize:longint;  {combined byte size of the files based upon clusters}
  224. begin
  225.   bytesize:=0;  {initialize byte size of the files as reported by DOS}
  226.   realsize:=0;  {initialize byte size of the files based upon clusters}
  227.   findfirst(fspec,hidden+readonly+sysfile+archive,dirinfo);
  228.   while doserror = 0 do                         {while files exist in the dir}
  229.   begin
  230.     bytesize:=bytesize+dirinfo.size;            {add byte sizes}
  231.     realsize:=realsize+((dirinfo.size div clust)*clust); {add clust sizes}
  232.     if dirinfo.size mod clust > 0 then          {if file partly fills a clust}
  233.       realsize:=realsize+clust;                 {add a whole clust}
  234.     findnext(dirinfo);                          {find the next file}
  235.   end;
  236.   if (totclust = 0) or (bytesize = 0) then
  237.   begin
  238.     {display the line for this directory (this is to avoid div by 0 case)}
  239.     writeln(bytesize:10,' ',realsize:10,' ',realsize-bytesize:10,' ',0.0:6:2,'%');
  240.   end else
  241.   begin
  242.     {display the line for this directory}
  243.     case usrrptm of
  244.       1:writeln(bytesize:10,' ',realsize:10,' ',realsize-bytesize:10,' ',((((realsize-bytesize)/clust)/totclust)*100):6:2,'%');
  245.       2:writeln(bytesize:10,' ',realsize:10,' ',realsize-bytesize:10,' ',100*((realsize-bytesize)/realsize):6:2,'%');
  246.     end; {case}
  247.   end;
  248.   totalbs:=totalbs+bytesize; {add this directory bytesize to the total}
  249.   totalrs:=totalrs+realsize; {add this directory realsize to the total}
  250. end;
  251.  
  252.  
  253. {This procedure will display the dir we are working on, calculate the space}
  254. {being wasted in that dir, and finally, recurse into any subdirectories}
  255. procedure showWasted(theDir:string);
  256. var
  257.   nextDir:string;      {var to hold the next dir before recursing into it}
  258.   dirinfo2:searchrec;  {searchrec for findfirst/findnext}
  259. begin
  260.   if length(theDir) > 39 then
  261.     checkpause(2)              {add 2 to linecount for long directories}
  262.   else checkpause(1);          {add 1 to the linecount and check for pause}
  263.   write(padr(theDir,39,' '));   {display the current dir}
  264.   calcwaste(fspec);    {calculate and display wasted space}
  265.   {find the first subdir in this dir}
  266.   findfirst(fspec,hidden+sysfile+readonly+archive+directory,dirinfo2);
  267.   while (quitit = false) and (doserror = 0) do {while no ESC key and a file was found (no error)}
  268.   begin
  269.     if dirinfo2.attr and directory = directory then  {if the file found is a directory...}
  270.     begin
  271.       nextDir:=dirinfo2.name;   {store the name as the next dir to recurse}
  272.       {only recurse non . and .. directories}
  273.       if (nextDir <> '.') and (nextDir <> '..') then
  274.       begin
  275.         nextDir:=fexpand(nextDir); {expand the next dir for display later}
  276.         ChDir(nextDir);         {change into the next directory}
  277.         showWasted(nextDir);    {call this procedure(showWasted) recursivly}
  278.         ChDir('..');            {change back to this directory}
  279.       end;
  280.     end;
  281.     findnext(dirinfo2);         {find the next file/directory}
  282.   end;
  283. end;
  284.  
  285.  
  286. {This procedure will display some initial stats and spawn off the initial}
  287. {call to the recursion procedure}
  288. {Munge, munj, n. To process information; think; muttle over.}
  289. procedure munge;
  290. begin
  291.   GetDir(0,origDir);       {find where we are on the drive so we can come back}
  292.   if s1name <> '' then     {if the user specified a starting point...}
  293.     startDir:=init(s1name) {init drive info for the users drive}
  294.   else
  295.     startDir:=init(origDir); {init drive info for the current drive}
  296.   {display cluster info and column headers}
  297.   writeln('Cluster size  :  ',clust:10);
  298.   writeln('# of Clusters :  ',totclust:10);
  299.   writeln('Est. Disk Size:  ',totclust*clust:10);
  300.   writeln('Directory                                Bytesize   Realsize     Wasted');
  301.   linecount:=6;
  302.   {$I-}
  303.   ChDir(dir(startDir));         {go into the starting directory}
  304.   if IOResult = 3 then syntax('Path not found: '+startDir);
  305.   {$I+}
  306.   showWasted(startDir);         {display wasted space in this dir and its subdirs}
  307.   if showtotals = true then     {display the total line if the user hasn't specified otherwise}
  308.   begin
  309.     write('Total:                                 ');
  310.     if (totclust = 0) or (totalbs = 0) then
  311.     begin
  312.       {display the line for this directory (this is to avoid div by 0 case)}
  313.       writeln(totalbs:10,' ',totalrs:10,' ',totalrs-totalbs:10,' ',0.0:6:2,'%');
  314.     end else
  315.     begin
  316.       {display the line for this directory}
  317.       case usrrptm of
  318.         1:writeln(totalbs:10,' ',totalrs:10,' ',totalrs-totalbs:10,' ',((((totalrs-totalbs)/clust)/totclust)*100):6:2,'%');
  319.         2:writeln(totalbs:10,' ',totalrs:10,' ',totalrs-totalbs:10,' ',100*((totalrs-totalbs)/totalrs):6:2,'%');
  320.       end; {case}
  321.     end;
  322.   end;
  323.   ChDir(origDir);          {go back to where we originally started from}
  324. end;
  325.  
  326.  
  327. begin
  328.   {display the program name, version, and author(s)}
  329.   writeln;
  330.   writeln('WASTED.EXE v',version,' - written by Tim Jones');
  331.   writeln;
  332.   fcount:=0;                 {set file counter to zero}
  333.   usrclust:=0;               {set user defined clustersize to 0}
  334.   {change the usrrptm value to a 2 to make the second report method the default}
  335.   usrrptm:=1;                {set the report method to 1 (default)}
  336.   showtotals:=true;          {set the flag to show the totals}
  337.   tmp:='';                   {init the tmp number holder}
  338.   for i:=1 to paramcount do  {loop through each command-line parameter}
  339.   begin
  340.     param:=upper(paramstr(i));  {store off the current parameter in uppercase}
  341.     if (left(param,1) = '/') or (left(param,1) = '-') then  {is it an option?}
  342.     begin
  343.       case param[2] of   {check the second character of the parameter}
  344.         '?':syntax('');  {help text}
  345.         'C':begin    {user specified cluster size}
  346.               {check for a colon separater and report any error}
  347.               if param[3] <> ':' then syntax('Unknown parameter:'+param);
  348.               {convert what follows the colon into a number}
  349.               val(right(param, length(param)-3), usrclust, error);
  350.               if error <> 0 then  {if the convertion faild...}
  351.               begin
  352.                 {display the conversion error and quit}
  353.                 syntax('Numeric conversion error in parameter:'+param);
  354.               end;
  355.               if usrclust <= 0 then  {if the cluster size is <=0...}
  356.               begin
  357.                 {display the cluster size error and quit}
  358.                 syntax('Cluster size must be >= 0');
  359.               end;
  360.             end;
  361.         'N':begin    {No Totals parameter?}
  362.               {check for a 'T'}
  363.               if param[3] <> 'T' then syntax('Unknown parameter:'+param);
  364.               showtotals:=false;     {turn off the showing of end totals}
  365.             end;
  366.         'P':begin    {Pause screen listing}
  367.               pause:=true;           {turn on the pausing between screenfulls}
  368.             end;
  369.         'R':begin    {report method was specified}
  370.               {check for a colon separater and report any error}
  371.               if param[3] <> ':' then syntax('Unknown parameter:'+param);
  372.               {convert what follows the colon into a number}
  373.               val(right(param, length(param)-3), usrrptm, error);
  374.               if error <> 0 then  {if the convertion faild...}
  375.               begin
  376.                 {display the conversion error and quit}
  377.                 syntax('Numeric conversion error in parameter:'+param);
  378.               end;
  379.               if usrrptm <= 0 then  {if the report method is <=0...}
  380.               begin
  381.                 {display the report method error and quit}
  382.                 syntax('Report method must be >= 0');
  383.               end;
  384.               if usrrptm > lastrptm then  {if the report method is too high...}
  385.               begin
  386.                 {display the report method error and quit}
  387.                 str(lastrptm,tmp);  {convert numeric report method to string}
  388.                 syntax('Report method must be <= '+tmp);
  389.               end;
  390.             end;
  391.         else
  392.           {the user specified an unknown parameter so display error and quit}
  393.           syntax('Unknown parameter:'+param);
  394.       end; {case}
  395.     end else
  396.     begin
  397.       {the user must have specified a filename / directory}
  398.       fcount:=fcount+1;
  399.       case fcount of  {this case stmt is here for future expansion}
  400.         1: s1name:=param;  {source 1 name = user specified starting directory}
  401.       else
  402.         {only one file parameter is allowed, display error and quit}
  403.         syntax('Too many file parameters:'+param);
  404.       end;
  405.     end;
  406.   end;
  407.   munge; {let's go!!!}
  408. end.
  409.  
  410. {Program History:
  411.  v1.0 - first (non public) release limited to 4096 byte cluster size
  412.  v1.1 + added auto calculating of cluster size
  413.       - changed wasted percentage to be percentage of used disk space
  414.  v1.2 - changed wasted percentage to be percentage of total disk space
  415.       - previous versions were fixed to C:
  416.       + added ability to start from any directory
  417.  v1.3 - first public release
  418.       - completely re-worked the checking of commandline parameters
  419.       - fixed a bug which ended the recursion early
  420.       + added help/syntax screen
  421.       + added /? option for display of help/syntax screen
  422.       + added /C:n option to allow the user to change the cluster size
  423.       + added this history list
  424.  v1.4 - fixed a bug which causes Runtime Error 003 (path not found) when
  425.         you are in a subdirectory and did not specify a starting directory.
  426.       + now you can specify paths with or without the trailing backslash
  427.       + added a Total line at the end (default is to show totals)
  428.       + added the switch /NT to disable the showing of totals
  429.       + added the switch /P to enable a pause between screenfulls of info
  430.  v1.5 + added the switch /R:n to allow the user to select the method
  431.         for calculating the wasted percentage
  432.         n=1 = Wasted percentage based upon disk size (default)
  433.         n=2 = Wasted percentage based upon used disk space
  434.       + added more information to the documentation file
  435.  v1.6 - the program will now recurse into hidden subdirectories
  436.       + recompiled to produce a smaller EXE (almost half the original size!)
  437. }
  438.